package hr.chus.alfresco.struts2.webscript;

import java.io.IOException;
import java.util.Iterator;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.dispatcher.Dispatcher;
import org.apache.struts2.dispatcher.mapper.ActionMapping;
import org.apache.struts2.dispatcher.ng.ExecuteOperations;
import org.apache.struts2.dispatcher.ng.HostConfig;
import org.apache.struts2.dispatcher.ng.InitOperations;
import org.apache.struts2.dispatcher.ng.PrepareOperations;
import org.apache.struts2.util.MakeIterator;
import org.springframework.extensions.webscripts.AbstractWebScript;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse;
import org.springframework.extensions.webscripts.servlet.WebScriptServletRequest;
import org.springframework.extensions.webscripts.servlet.WebScriptServletResponse;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.context.support.WebApplicationContextUtils;

/**
 * 
 * @author Jan Čustović (jan.custovic@gmail.com)
 *
 */
public class StrutsPrepareAndExecuteWebscript extends AbstractWebScript implements ServletContextAware {
    
    private static final Log LOG = LogFactory.getLog(StrutsPrepareAndExecuteWebscript.class);
    
    private boolean initialized;
    private PrepareOperations prepare;
    private ExecuteOperations execute;

    private ServletContext servletContext;
	
    private final synchronized void init() {
        if (!initialized) {
            LOG.info("Initializing struts2-alfresco-plugin...");
            InitOperations init = new InitOperations();
            try {
                WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
                HostConfig hostConfig = new HostConfig() {

                    @Override
                    public ServletContext getServletContext() {
                        return servletContext;
                    }

                    @SuppressWarnings("unchecked")
                    @Override
                    public Iterator<String> getInitParameterNames() {
                        return MakeIterator.convert(new String[0]);
                    }

                    @Override
                    public String getInitParameter(String name) {
                        return null;
                    }

                };
                init.initLogging(hostConfig);
                Dispatcher dispatcher = init.initDispatcher(hostConfig);
                init.initStaticContentLoader(hostConfig, dispatcher);

                prepare = new PrepareOperations(servletContext, dispatcher);
                execute = new ExecuteOperations(servletContext, dispatcher);

                initialized = true;
            } finally {
                init.cleanup();
            }
        }
    }

    public final void destroy() {
        if (initialized) {
            prepare.cleanupDispatcher();
            initialized = false;
        }
    }

	@Override
	public final void execute(WebScriptRequest req, WebScriptResponse res) throws IOException {
	    WebScriptServletRequest origReq = (WebScriptServletRequest) req;
	    WebScriptServletResponse origRes = (WebScriptServletResponse) res;
	    
	    HttpServletRequest request = new WebScriptRequestWrapper(origReq);
	    HttpServletResponse response = new WebScriptResponseWrapper(origRes);
		
		// Will only init once.
		init();

		try {
			prepare.setEncodingAndLocale(request, response);
			prepare.createActionContext(request, response);
			prepare.assignDispatcherToThread();

			request = prepare.wrapRequest(request);
            ActionMapping mapping = prepare.findActionMapping(request, response, true);
            if (mapping == null) {
                boolean handled = execute.executeStaticResourceRequest(request, response);
                if (!handled) {
                    LOG.warn("Mapping for request " + origReq.getServerPath() + " not resolved.");
                }
            } else {
                execute.executeAction(request, response, mapping);
            }
		} catch (ServletException e) {
		    LOG.error(e.getMessage(), e);
		} finally {
			prepare.cleanupRequest(request);
		}
	}

	// Getters & setters
	
    @Override
    public final void setServletContext(ServletContext servletContext) {
        this.servletContext = servletContext;
    }

}
